package gipfProject.games.zertz.data;

public class RuleData {
	ZertzData gameState;
	
	public RuleData(ZertzData gameState){
		this.gameState = gameState;
	}
	
	/**
	 * Checks whether we can place a marble at this location
	 * 
	 * @param col
	 * @param row
	 * @return true if we can place here; false otherwise
	 */
	public boolean canPlaceMarble(int col, int row) {
		// Fails if not an empty ring
		if (!gameState.board[col][row].isEmpty()) {
			gameState.setErrorMessage("That is not an empty ring!");
			return false;
		}

		//Fails if there are not available marbles to place
		if (gameState.isPoolEmpty()){
			//If the pool is empty, we have to have such a marble previously captured
			if ( gameState.getScore(gameState.getCurrentPlayer(), gameState.getPlaceType()) <= 0) {
				gameState.setErrorMessage("You don't have any of those!");
				return false;
			}
		}else{
			if (gameState.getPool(gameState.getPlaceType()) <= 0){
				gameState.setErrorMessage("The pool doesn't have any of those!");
				return false;
			}
		}
		
		// Otherwise succeeds
		return true;
	}
	
	/**
	 * Checks if we could place a marble of any available color in the specified location
	 * 
	 * @param col
	 * @param row
	 * @return false if the location is occupied or there are NO available marbles; true otherwise
	 */
	
	private boolean canPlaceAnyMarble(int col, int row){
		// Fails if not an empty ring
		if (!gameState.board[col][row].isEmpty()) {
			return false;
		}

		//Fails if there are not available marbles to place
		if (gameState.isPoolEmpty() &&
			gameState.getScore(gameState.getCurrentPlayer(), 0) == 0 &&
			gameState.getScore(gameState.getCurrentPlayer(), 1) == 0 && 
			gameState.getScore(gameState.getCurrentPlayer(), 2) == 0) 
		{

			return false;
		}
		
		// Otherwise succeeds
		return true;
	}

	/**
	 * Checks whether a piece is a valid ring to remove
	 * 
	 * @param col
	 * @param row
	 * @return null if the piece can be removed; error message otherwise
	 */
	public boolean canRemoveRing(int col, int row) {
		// Fails if not an empty ring
		if (!gameState.board[col][row].isEmpty()) {
			gameState.setErrorMessage("That is not an empty ring!");
			return false;
		}


		//Check if we are able to slide the ring out from the board
		 
		if (!canRingSlideOut(col,row)) {
			// If no gap, return an error message
			gameState.setErrorMessage("Can only remove edge pieces!");
			return false;
		}

		// Otherwise succeeds
		return true;
	}
	
	public boolean canCapture(int colStart, int rowStart, int colEnd, int rowEnd){
		//Must move in a straight line
		Location oldLocation = new Location (colStart,rowStart);
		Location newLocation = new Location (colEnd,  rowEnd);
		int direction = oldLocation.directionTo(newLocation);

		Location testLocation = oldLocation.moveDirection(direction,1) ;

		//Must move to an empty square
		if (!gameState.board[newLocation.x][newLocation.y].isEmpty())
		{
			gameState.setErrorMessage("Must jump to an empty square");
			return false;
		}
		
		//Must move in a straight line
		if(!oldLocation.validDirection(newLocation))
		{
			gameState.setErrorMessage("Captures must be in a straight line");
			return false;
		}

		//The square you jump over has to have a piece in it
		if (!gameState.board[testLocation.x][testLocation.y].isMarble())
		{
			gameState.setErrorMessage("Must jump over a marble");
			return false;
		}

		//Must move exactly two squares

		if(oldLocation.distanceTo(newLocation) != 2)
		{
			gameState.setErrorMessage("Captures can only jump one space!");
			return false;
		}

		//All rules passed
		return true;
	}//End of canCapture
	
	
	
	public boolean canSelect(int col, int row) {	
		// Fails if not a marble
		if (!gameState.board[col][row].isMarble()) {
			gameState.setErrorMessage("Must select a marble to capture with!");
			return false;
		}
		
		// Piece must have available captures
		if (!canPieceCapture(col,row)){
			gameState.setErrorMessage("That piece has no available captures!");
			return false;			
		}
		// Otherwise succeeds
		return true;
	}//End of canSelect
	
	/**
	 * Check if we are able to slide the ring out from the board
	 * This requires there to be two adjacent gaps for it to slide out from
	 */
	public boolean canRingSlideOut(int col, int row){

		boolean foundGap = false;
		boolean[] openings = new boolean[6];
		Location startLocation = new Location(col, row);

		//For each direction
		for (int x = 0; x < 6; x++) {

			//Offset by 1 for direction, as it uses 1-6, rather than 0-5
			Location temp = startLocation.moveDirection(x + 1, 1);

			if (validLocation(temp)) {
				openings[x] = !(gameState.board[temp.x][temp.y].isValid());
			} else {
				openings[x] = true;
			}
		}

		// Check if directions 0 and 5 match
		if (openings[0] && openings[5]) {
			foundGap = true;
		}

		// Check all other adjacent combinations
		for (int x = 0; x < 5; x++) {
			if (openings[x] && openings[x + 1]) {
				foundGap = true;
			}
		}

		// Return the results
		return foundGap;
	}
	
	/**
	 * Tests whether there is any ring that can be removed from the board
	 * @return true if at least one ring can potentially be removed, false otherwise.
	 */
	public boolean canAnyoneRemove(){
		boolean foundRemoval = false;
		
		//Check each square on the board until we find a gap
		for (int x = 0; x < ZertzData.boardCols  && !foundRemoval; x++){
			for (int y = 0; y < ZertzData.boardRows && !foundRemoval ; y++){
				
				//Check if it is valid
				if (gameState.board[x][y].isValid()){
					
					//Check if it can capture
					if(canRemoveRing(x,y)){
						foundRemoval = true;
					}
				}		
			}
		}
		
		//Clear any error messages generated by the canRemoveRing tests
		gameState.clearErrorMessage();
		
		//Otherwise no valid removals were found
		return foundRemoval;
	}
	
	public boolean canAnyoneCapture(){
		boolean foundCapture = false;	
		
		//For each square on the board
		for (int x = 0; x < ZertzData.boardCols  && !foundCapture; x++){
			for (int y = 0; y < ZertzData.boardRows && !foundCapture ; y++){
				
				//Check if it is valid
				if (gameState.board[x][y].isMarble()){
					
					//Check if it can capture
					if(canPieceCapture(x,y)){
						foundCapture = true;
					}
				}		
			}
		}
		
		//Clear any error messages generated by the canRemoveRing tests
		gameState.clearErrorMessage();
		
		//Otherwise no valid removals were found
		return foundCapture;
	}
	
	public boolean canPieceCapture(int col, int row){
		Location location = new Location(col,row);

		//Ensure this is actually a marble
		if (!gameState.getPiece(col,row).isMarble()){
			return false;
		}
		
		//Check if there's a piece in each direction
		for (int direction=1;direction<=6;direction++)
		{
			//Check 1 square in that direction
			Location testLocation = location.moveDirection(direction,1);

			if (validLocation(testLocation))
			{
				//Check if it's a marble
				if (gameState.board[testLocation.x][testLocation.y].isMarble())
				{
					//If it is, get the location beyond it
					Location farLocation = testLocation.moveDirection(direction,1);

					if (validLocation(farLocation)){
					
						//And check if that is empty   
						if (gameState.board[farLocation.x][farLocation.y].isEmpty())   
						{
							//If so, we've found a capture!
							return true;
						}
					}
				}
			}
		}
		//We checked everything and found nothing valid
		return false;
		
	}//End of pieceCanCapture
	
	/**
	 * Checks whether the location is within the bounds of the board
	 * 
	 * @param testLocation
	 * @return true if it is within the board; false otherwise
	 */
	public boolean validLocation(Location testLocation) {
		// Check that they are within the actual generated board
		return !(testLocation.x < 0 || testLocation.x > ZertzData.boardCols - 1
				|| testLocation.y < 0 || testLocation.y > ZertzData.boardRows - 1);

	}
	
	/**
	 * Checks whether the current player has any legal moves available
	 * 
	 * @return true if at least one move; false otherwise
	 */
	public Boolean anyValidMoves(){
		boolean foundMove = false;	
		
		//For each square on the board
		for (int x = 0; x < ZertzData.boardCols  && !foundMove; x++){
			for (int y = 0; y < ZertzData.boardRows && !foundMove ; y++){
				
				//Check if it is valid
				if (gameState.board[x][y].isValid()){
					
					if (gameState.moveType == ZertzData.captureMarble){
						//Check if it can capture
						if(canPieceCapture(x,y)){
							foundMove = true;
						}
					}
					else if (gameState.moveType == ZertzData.placeMarble){
						//Check if we can place a marble here
						foundMove = canPlaceAnyMarble(x,y);
					}
					else if (gameState.moveType == ZertzData.removeRing){
						//Check if we can remove this ring
						foundMove = canRemoveRing(x,y);
					}
					else{
						//This should never occur
						
					}
				}		
			}
		}
		
		//Clear any error messages generated by the canRemoveRing tests
		gameState.clearErrorMessage();
		
		//Otherwise no valid removals were found
		return foundMove;
	}
}
